#include "camera.h"
#include "film.h"

Camera::Camera(const Transform &w2c, float cNear, float cFar, float sOpen, float sClose, Reference<Film> fm) {
	worldToCamera = w2c;
	cameraToWorld = w2c.Inverse();
	clipNear = cNear;
	clipFar = cFar;
	shutterOpen = sOpen;
	shutterClose = sClose;
	film = fm;
	position = cameraToWorld(Point3(0,0,0));
}
Camera::~Camera() {
	// Decrease reference to the original Film pointer
	// This seems unnecessary, because when camera run out of scope, it will call automatically
	film = NULL;
}
void Camera::SetFilm(Reference<Film> fm) {
	film = fm;
}
float Camera::GenerateDifferentialRay(const Point2 &imageSample, const Point2 &lensSample, 
									  float timeSample, RayDifferential *ray) const {
	
	float wt = GenerateRay(imageSample, lensSample, timeSample, (Ray*)ray);
	
	// Find ray in x direction
	Point2 tmp = imageSample;
	tmp.x += 1;
	Ray rx;
	float wtx = GenerateRay(tmp, lensSample, timeSample, &rx);
	ray->rxOrigin = rx.o;
	ray->rxDirection = rx.d;

	// Find ray in y direction
	tmp = imageSample; 
	tmp.y += 1;
	Ray ry;
	float wty = GenerateRay(tmp, lensSample, timeSample, &ry);
	ray->ryOrigin = ry.o;
	ray->ryDirection = ry.d;
	
	if(wtx == 0.0 || wty == 0.0)
		return 0.0;

	ray->hasDifferentials =  true;
	return wt;
}

ProjectiveCamera::ProjectiveCamera(const Transform &w2c, const Transform &c2s,
								   float cNear, float cFar, float sOpen, float sClose, 
								   float lensr, float focald, Film *fm) : 
									Camera(w2c, cNear, cFar, sOpen, sClose, fm) {
	cameraToScreen = c2s;
	worldToScreen = cameraToScreen * worldToCamera;
	screenToWorld = worldToScreen.Inverse();
	lensRadius = lensr;
	focalDistance = focald;
	// set up other transform
	Point2 filmSize= fm->GetSize();
	float aspect = (float) filmSize.x / filmSize.y;
	if(aspect > 1.0) {
		// now we have Y [-1, 1], X [-aspect, aspect]
		// first translate [-aspect, 1] to [0, 0]
		// then map Y to [0, 1]
		// finally map XY to [0, Resolution]
		screenToRaster = 
			Scale((float)filmSize.x,(float)filmSize.y, 1.f) 
			* Scale(1./(2*aspect), -.5f, 1.f) 
			* Translate(aspect, -1.f, 0);
	
	} else {
		// now we have Y [-1, 1], X [-aspect, aspect]
		// first translate [-1, 1 / aspect ] to [0, 0]
		// then map X to [0, 1]
		// finally map XY to [0, Resolution]
		screenToRaster = 
			Scale((float)filmSize.x, (float)filmSize.y, 1) 
			* Scale(0.5f, -0.5f * aspect, 1.f) 
			* Translate(1.0, -1 / aspect, 0);
	}

	rasterToScreen = screenToRaster.Inverse();
	rasterToCamera = cameraToScreen.Inverse() * rasterToScreen;
	worldToRaster = screenToRaster * worldToScreen;
	rasterToWorld = screenToWorld * rasterToScreen;
}